#include "VTTTileStore.h"

#include "VTTAtlas.h"
#include "VTTCommon.h"
#include "VTTErrorManager.h"
#include "VTTImage.h"
#include "VTTInfoFile.h"
#include "VTTMemoryManager.h"
#include "VTTTimer.h"

#include <algorithm>
#include <array>
#include <iostream>
#include <map>
#include <math.h>

#include <boost/thread/condition.hpp>
#include <boost/cstdint.hpp>

using std::cout;
using std::endl;
using std::map;
using std::pair;
using std::set;
using std::string;
using std::vector;

using std::max;
using std::min;

///////////////////////////////////////////////////////////////////////////////

// LOCAL FUNCTIONS BEGIN

void GetGaussianFilterKernel( const UINT32 &width, const UINT32 &height, const float &sigma, float *kernel );

const unsigned char *GetPartDataPointer( const bool &scaledDirUsed,
	string &currentPartName, string &currentPartPath, const string &scaledPath,
	const string &fileFormat,
	const VTTAtlas &atlas, map<string, VTTImage*> &atlasPartMap,
	const UINT32 &times, const UINT32 &pX, const UINT32 &pY);

// bool ScaleRGBImageToTheHalf( unsigned char *dest, const unsigned char *src,
// 	UINT32 srcWidth );

// LOCAL FUNCTIONS END

///////////////////////////////////////////////////////////////////////////////

/**
  * Constructs a new VTTTileStore object with a VTTAtlas object as its base
  * The atlas object is copied to be independent of the existing object.
  *
  * @param atlas const reference to an existing VTTAtlas object which is used as the base of the Tile Store
  */
VTTTileStore::VTTTileStore( const VTTAtlas &atlas )
{
	mIsUpdating = false;
	mIsWriting = false;
	mIsWritingAtlasAndTileStore = false;
	mUpdateResult = true;
	mWriteResult = true;
	mNumComponents = 3;

	if (atlas.IsNull())
	{	
		//cout << "ERROR: VTTTileStore can not be initialized with NULL-VTTAtlas"
		//	 << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::VTTTileStore(const VTTAtlas &): VTTTileStore can not be initialized with Null-Atlas" );
		mAtlas = 0;
		mIsNull = true;
		return;
	}

	mAtlas = new VTTAtlas( atlas );
	mIsNull = false;

	mFullPath = mAtlas->GetConfiguration().GetBasePath() + 
		VTT_FILE_SEPARATOR + mAtlas->GetConfiguration().GetTileStoreName();
}

/**
  *	Destroys the VTTTileStore object
  */
VTTTileStore::~VTTTileStore( void )
{
	if (mAtlas) delete mAtlas;
}

/**
  *	Method that is used for writing and updating the Tile Store to hard disc.
  *
  * @param overwrite boolean value indicating whether the existing files should be overwritten or not
  * @param update boolean value indicating whether the call is for updating reasons or not
  * @param tilesToConsider map, that maps Atlas part coordinates to all the tile coordinates (in part space), that have to be considered in the writing or updating procedure
  * @return boolean value indicating whether the writing or updating procedure was successful or not
  */
bool VTTTileStore::DoIt( bool overwrite, bool update, map<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>> &tilesToConsider )
{
	string scaled_path = mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "scaled";
	string atlas_format =
		GetFileFormatEnding( mAtlas->GetConfiguration().GetAtlasFormat() );
	string tiles_format =
		GetFileFormatEnding( mAtlas->GetConfiguration().GetTilesFormat() );

	UINT32 final_tile_size = mAtlas->GetConfiguration().GetTileSize();
	UINT32 num_elements_in_single_tile = final_tile_size *
		final_tile_size * mNumComponents;
	UINT32 num_elements_in_merged_part = 4 * num_elements_in_single_tile;
	UINT32 num_elements_in_scaled_part = (mAtlas->GetPartSideLength() *
		mAtlas->GetPartSideLength() / 4) * mNumComponents;
	unsigned char *single_tile =
		static_cast<unsigned char*>( malloc( num_elements_in_single_tile ) );

	unsigned char *merged_part =
		static_cast<unsigned char*>( malloc( num_elements_in_merged_part ) );

	unsigned char *scaled_part =
		static_cast<unsigned char*>( malloc( num_elements_in_scaled_part ) );

	if (!single_tile || !merged_part || !scaled_part)
	{
		//cout << "ERROR: Not enough memory for Tile Store generation" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Not enough memory for Tile Store generation" );

		if (single_tile) free( single_tile );
		if (merged_part) free( merged_part );
		if (scaled_part) free( scaled_part );

		return false;
	}

	VTTTimer timer;
	double time = 0.0;

	UINT32 border = mAtlas->GetConfiguration().GetBorder();
	UINT32 tile_size = final_tile_size - 2 * border;

	bool merged = false;
	bool parts_update_necessary = false;
	bool scaled_dir_used = false;

	map<string, VTTImage*> atlas_part_map;
	map<string, VTTImage*>::iterator it;

	UINT32 merged_part_size = 2 * tile_size;
	UINT32 merged_part_inc = merged_part_size * mNumComponents;
	UINT32 merged_part_index = 0;
	UINT32 num_tiles_to_write = 0;
	UINT32 part_size = mAtlas->GetPartSideLength();
	UINT32 parts_per_side = mAtlas->GetPartsPerSide();
	UINT32 size = mAtlas->GetSideLength();
	UINT32 tile_count_at_all = 0;

	UINT32 tiles_per_part = part_size / tile_size;
	UINT32 times = 0;

	UINT32 helper = mAtlas->GetPartsPerSide() * mAtlas->GetPartsPerSide() *
		tiles_per_part * tiles_per_part;

	while (helper >= 1)
	{
		num_tiles_to_write += helper;
		helper /= 4;
	}

	const unsigned char *data_pointer = 0;

	UINT32 x = 0;
	UINT32 x_coord = 0;
	UINT32 y = 0;
	UINT32 y_coord = 0;
	

	UINT32 num_parts = 0;
	UINT32 part_count = 0;
	UINT32 part_index = 0;
	UINT32 part_inc = 0;
	UINT32 final_tile_inc = final_tile_size * mNumComponents;
	UINT32 tile_inc = final_tile_inc - 2 * border * mNumComponents;
	UINT32 tile_index = 0;

	UINT32 pX = 0;
	UINT32 pY = 0;

	UINT32 tiles_for_level = 0;
	UINT32 tile_count = 0;

	UINT32 tile_x_coord = 0;
	UINT32 tile_y_coord = 0;

	string current_part_name;
	string current_part_path;
	string base_dir;
	string generate_message;

	string tile_name;
	string tile_path;

	VTTImage *image = 0;
	VTTImage *current_part = 0;

	UINT32 readable_tile_width = 0;
	UINT32 readable_tile_height = 0;

	bool upper_left_copied = false;
	bool upper_right_copied = false;
	bool lower_left_copied = false;
	bool lower_right_copied = false;

	std::tr1::array<UINT32, 2> new_key;
	std::tr1::array<UINT32, 2> element;
	map<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>> map_helper;
	map<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>>::iterator tile_it;
	map<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>>::iterator helper_it;
	set<std::tr1::array<UINT32, 2>>::iterator set_it;

	while (size >= tile_size)
	{
		// Merge atlas parts if they are too small
		if (parts_per_side > 1 && part_size < (times != 0 ? (2 * tile_size) : tile_size))
		{
			//y_coord = 0;
			part_count = 0;
			//num_parts = (parts_per_side * parts_per_side) / 4;
			num_parts = static_cast<UINT32>( tilesToConsider.size() );

			//for (UINT32 y = 0; y < parts_per_side; y += 2)
			//{
				//x_coord = 0;

			//for (UINT32 x = 0; x < parts_per_side; x += 2)
			time = 0.0;
			timer.Toc();

			for (tile_it = tilesToConsider.begin(); tile_it != tilesToConsider.end(); tile_it++)
			{
				x_coord = tile_it->first[0] / 2u;
				y_coord = tile_it->first[1] / 2u;

				part_count += 1;

				//  + str(round((float(partCount) / float(numParts)) * 100, 2)) + '%)',

				time += timer.Toc();

				if (time >= 2.0 || part_count == tilesToConsider.size())
				{
					cout << "\r-\tMerging Atlas parts: " << 
						UINT32ToString( part_count ) << "/" <<
						UINT32ToString( num_parts ) << "(" <<
						UINT32ToString( static_cast<UINT32>( floor( (static_cast<float>( part_count ) / static_cast<float>( num_parts )) * 100.0f ) ) )
						<< "%)";
					flush( cout );

					time -= 2.0;
				}

				if (overwrite || !IsFile( scaled_path + VTT_FILE_SEPARATOR + "_texture_atlas_" + UINT32ToString( times ) + "_" + UINT32ToString( x_coord ) + "_" + UINT32ToString( y_coord ) + atlas_format ))
				{
					// Reset merged_part to fully white color
					// => Not necessary, all pixels are used
					// memset( merged_part, 255, num_elements_in_merged_part );

					for (UINT32 yOffset = 0; yOffset < 2; yOffset++)
					{
						for (UINT32 xOffset = 0; xOffset < 2; xOffset++)
						{

							data_pointer =
								GetPartDataPointer( scaled_dir_used,
									current_part_name, current_part_path,
									scaled_path, atlas_format, *mAtlas,
									atlas_part_map, times - 1,
									tile_it->first[0] - (tile_it->first[0] % 2) + xOffset,
									tile_it->first[1] - (tile_it->first[1] % 2) + yOffset );

							if (!data_pointer)
							{
								VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part image" );

								free( single_tile );
								free( merged_part );
								free( scaled_part );

								VTTMemoryManager::Get()->FreeAllMemory();

								for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
								{
									delete (*it).second;

									it = atlas_part_map.erase(it);
								}

								return false;
							}

							merged_part_index = 
								(yOffset * tile_size * merged_part_inc) +
								(xOffset * tile_size * mNumComponents);

							part_index = 0;
							part_inc = tile_inc;

							for (UINT32 y_in_tile= 0;
								y_in_tile < final_tile_size; y_in_tile++)
							{
								memcpy( &merged_part[merged_part_index],
									&data_pointer[part_index], part_inc );

								merged_part_index += merged_part_inc;
								part_index += part_inc;
							}
						}
					}

					current_part_name = "_texture_atlas_" +
						UINT32ToString( times ) + "_" +
						UINT32ToString( x_coord ) + "_" +
						UINT32ToString( y_coord ) + atlas_format;

					current_part_path = scaled_path +
						VTT_FILE_SEPARATOR + current_part_name;

					VTTTileStore::ScaleRGBImageToTheHalf( scaled_part,
						merged_part, merged_part_size );

					image = new VTTImage( scaled_part, part_size,
						part_size, mNumComponents );

					atlas_part_map.insert(
						pair<string, VTTImage*>( current_part_name, image ) );

					if (!VTTImage::WriteImageFile( current_part_path,
						scaled_part, part_size, part_size, mNumComponents,
						mAtlas->GetConfiguration().GetPictureQuality()))
					{

						VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Could not write merged Atlas part " + current_part_path );

						free( single_tile );
						free( merged_part );
						free( scaled_part );

						VTTMemoryManager::Get()->FreeAllMemory();

						for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
						{
							delete (*it).second;

							it = atlas_part_map.erase(it);
						}

						return false;
					}
				}
				x_coord += 1;
			}

			y_coord += 1;
			//}

			scaled_dir_used = true;
			merged = true;
			parts_per_side /= 2;
			parts_update_necessary = true;
			//part_size *= 2;
		}

		tiles_per_part = (0 != times && !merged ? part_size / 2 : part_size) / tile_size;

		if (parts_update_necessary)
		{
			map_helper.clear();

			for (tile_it = tilesToConsider.begin(); tile_it != tilesToConsider.end(); tile_it++)
			{
				new_key[0] = tile_it->first[0] / 2u;
				new_key[1] = tile_it->first[1] / 2u;

				helper_it = map_helper.find( new_key );

				if (map_helper.end() == helper_it)
				{
					helper_it = map_helper.insert( pair<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>>( new_key, set<std::tr1::array<UINT32, 2>>() ) ).first;
				}

				//helper_it->second.insert( );
				//map_helper[new_key].insert( (x[0] + (key[0] % 2) * (tilesPerPart / 2), x[1] + (key[1] % 2) * (tilesPerPart / 2)) );

				for (set_it = tile_it->second.begin(); set_it != tile_it->second.end(); set_it++)
				{
					element[0] = (*set_it)[0] + (tile_it->first[0] % 2) * (tiles_per_part / 2);
					element[1] = (*set_it)[1] + (tile_it->first[1] % 2) * (tiles_per_part / 2);

					helper_it->second.insert( element );
				}
			}

			tilesToConsider.clear();
			tilesToConsider = map_helper;
			parts_update_necessary = false;
		}

		if (0 != times)
		{
			if (!merged)
			{	
				part_size /= 2;
				part_count = 0;
				//num_parts = parts_per_side * parts_per_side;
				num_parts = static_cast<UINT32>( tilesToConsider.size() );

				//for (UINT32 y = 0; y < parts_per_side; y++)
				//{
				//for (UINT32 x = 0; x < parts_per_side; x++)
				time = 0.0;
				timer.Toc();

				for (tile_it = tilesToConsider.begin(); tile_it != tilesToConsider.end(); tile_it++)
				{
					part_count++;

					time += timer.Toc();
					if (time >= 2.0 || part_count == tilesToConsider.size())
					{
						cout << "\r-\tScaling Atlas parts to the half: " <<
							UINT32ToString( part_count ) << "/" <<
							UINT32ToString( num_parts ) << " (" <<
							UINT32ToString( static_cast<UINT32>( floor( (static_cast<float>( part_count ) / static_cast<float>( num_parts )) * 100.0f ) ) )
							<< "%)";
						flush( cout );

						time -= 2.0;
					}

					if (overwrite || merged || !IsFile( scaled_path + VTT_FILE_SEPARATOR +
						"_texture_atlas_" + UINT32ToString( times ) + "_" +
						UINT32ToString( tile_it->first[0] ) + "_" + UINT32ToString( tile_it->first[1] ) + atlas_format ))
					{

						data_pointer =
							GetPartDataPointer( scaled_dir_used,
								current_part_name, current_part_path,
								scaled_path, atlas_format, *mAtlas,
								atlas_part_map,
								times - (merged ? 0 : 1), tile_it->first[0], tile_it->first[1] );

						if (!data_pointer)
						{
							VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part image" );

							free( single_tile );
							free( merged_part );
							free( scaled_part );

							VTTMemoryManager::Get()->FreeAllMemory();

							for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
							{
								delete (*it).second;

								it = atlas_part_map.erase(it);
							}

							return false;
						}

						VTTTileStore::ScaleRGBImageToTheHalf( scaled_part, data_pointer,
							(*it).second->GetWidth() );

						current_part_name = "_texture_atlas_" +
							UINT32ToString( times ) + 
							"_" + UINT32ToString( tile_it->first[0] ) + "_" +
							UINT32ToString( tile_it->first[1] ) + atlas_format;

						current_part_path = scaled_path +
							VTT_FILE_SEPARATOR + current_part_name;

						image = new VTTImage( scaled_part, part_size,
							part_size, mNumComponents );

						atlas_part_map.insert(
							pair<string, VTTImage*>( current_part_name, image ) );

						if (!VTTImage::WriteImageFile( current_part_path,
							scaled_part, part_size, part_size, mNumComponents,
							mAtlas->GetConfiguration().GetPictureQuality()))
						{
							VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Could not write scaled Atlas part " + current_part_path );

							free( single_tile );
							free( merged_part );
							free( scaled_part );

							VTTMemoryManager::Get()->FreeAllMemory();

							for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
							{
								delete (*it).second;

								it = atlas_part_map.erase(it);
							}

							return false;
						}
					}
				}
				//}
				scaled_dir_used = true;
			}

			merged = false;
		}

		base_dir = mFullPath + VTT_FILE_SEPARATOR +"tiles_b" +
			UINT32ToString( border ) + "_level" + UINT32ToString( times );


		if (!IsDir( base_dir ))
		{
			if (update)
			{
				//cout << "ERROR: " << base_dir << " does not exist" << endl;

				VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Tile Store sub directory" + base_dir + " does not exist");

				free( single_tile );
				free( merged_part );
				free( scaled_part );

				VTTMemoryManager::Get()->FreeAllMemory();

				for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
				{
					delete (*it).second;

					it = atlas_part_map.erase(it);
				}

				return false;
			}

			if (!update && !MakeDir( base_dir ))
			{
				//cout << "ERROR: Could not create dir " << base_dir << endl;

				VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Could not create Tile Store sub directory " + base_dir );

				free( single_tile );
				free( merged_part );
				free( scaled_part );

				VTTMemoryManager::Get()->FreeAllMemory();

				for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
				{
					delete (*it).second;

					it = atlas_part_map.erase(it);
				}

				return false;
			}
		}

		//tiles_per_part = part_size / tile_size;
		generate_message = "- \tLevel " + UINT32ToString( times ) + ": ";
		//tiles_for_level = parts_per_side * parts_per_side *
		//	tiles_per_part * tiles_per_part;

		tiles_for_level = 0;
		for (tile_it = tilesToConsider.begin(); tile_it != tilesToConsider.end(); tile_it++)
		{
			tiles_for_level += static_cast<UINT32>( tile_it->second.size() );
		}

		tile_count = 0;

		part_inc = part_size * mNumComponents;

		//for (UINT32 pY = 0; pY < parts_per_side; pY++)
		//{
		//for (UINT32 pX = 0; pX < parts_per_side; pX++)

		time = 0.0;
		timer.Toc();

		for (tile_it = tilesToConsider.begin(); tile_it != tilesToConsider.end(); tile_it++)
		{

			pX = tile_it->first[0];
			pY = tile_it->first[1];

			if (scaled_dir_used)
			{
				current_part_name = "_texture_atlas_" +
					UINT32ToString( times ) + "_" +
					UINT32ToString( pX ) + "_" +
					UINT32ToString( pY ) + atlas_format;

				current_part_path = scaled_path +
					VTT_FILE_SEPARATOR + current_part_name;
			} else
			{
				current_part_name = "_texture_atlas_" +
					UINT32ToString( pX ) + "_" +
					UINT32ToString( pY ) + atlas_format;
				current_part_path = mAtlas->GetFullPath() +
					VTT_FILE_SEPARATOR + current_part_name;
			}

			it = atlas_part_map.find( current_part_name );

			if (atlas_part_map.end() == it)
			{
				// Atlas part is not in the map, add it
				it = atlas_part_map.insert(
					pair<string, VTTImage*>( 
					current_part_name,
					new VTTImage( current_part_path ) )
					).first;
			}

			current_part = (*it).second;

			if (!current_part->HasInternalData())
			{
				current_part->Load();
			}

			data_pointer = current_part->GetDataPointer();

			if (!data_pointer)
			{
				VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

				free( single_tile );
				free( merged_part );
				free( scaled_part );

				VTTMemoryManager::Get()->FreeAllMemory();

				for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
				{
					delete (*it).second;

					it = atlas_part_map.erase(it);
				}

				return false;
			}

			// Generate all tiles for the part:
			//for (UINT32 y = 0; y < tiles_per_part; y++)
			//{
			//for (UINT32 x = 0; x < tiles_per_part; x++)
			for (set_it = tile_it->second.begin(); set_it != tile_it->second.end(); set_it++)
			{
				x = (*set_it)[0];
				y = (*set_it)[1];

				tile_count++;
				tile_count_at_all++;

				time += timer.Toc();
				if (time > 2.0 || tile_count == tiles_for_level)
				{

					cout << "\r" << generate_message <<
						UINT32ToString( tile_count ) << "/" <<
						UINT32ToString( tiles_for_level ) <<
						" (" << UINT32ToString( static_cast<UINT32>( floor( (static_cast<float>( tile_count ) / static_cast<float>( tiles_for_level )) * 100.0f ) ) ) <<
						"%), all in all: " << 
						UINT32ToString( tile_count_at_all ) << "/" <<
						UINT32ToString( num_tiles_to_write ) << " (" <<
						UINT32ToString( static_cast<UINT32>( floor( (static_cast<float>( tile_count_at_all ) / static_cast<float>( num_tiles_to_write )) * 100.0f ) ) ) << "%)";
					flush( cout );

					time -= 2.0;
				}

				tile_x_coord = pX * tiles_per_part + x;
				tile_y_coord = pY * tiles_per_part + y;

				if (update)
					mUpdateResults.push_back( VTTTileInfo( tile_x_coord, tile_y_coord, times ) );
					
				tile_name = "tile_" + UINT32ToString( times ) + "_" + UINT32ToString( tile_x_coord ) + "_" + UINT32ToString( tile_y_coord ) + tiles_format;
				tile_path = base_dir + VTT_FILE_SEPARATOR + tile_name;

				if (!overwrite && IsFile( tile_path ))
				{
					continue;
				}

				// Set tile to white
				// => Not necessary, cause all pixels are used
				// memset( single_tile, 255, num_elements_in_single_tile );

				if (0 == border)
				{
					tile_index = 0;
					part_index = y * tile_size * part_inc +
						x * tile_size * mNumComponents;

					for (UINT32 y_in_tile= 0;
						y_in_tile < final_tile_size; y_in_tile++)
					{
						memcpy( &single_tile[tile_index],
							&data_pointer[part_index], tile_inc );

						tile_index += tile_inc;
						part_index += part_inc;
					}
				} else
				{
					// border > 0

					upper_left_copied = false;
					upper_right_copied = false;
					lower_left_copied = false;
					lower_right_copied = false;

					if (!current_part->HasInternalData())
					{
						current_part->Load();
					}

					data_pointer = current_part->GetDataPointer();

					if (!data_pointer)
					{
						VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

						free( single_tile );
						free( merged_part );
						free( scaled_part );

						VTTMemoryManager::Get()->FreeAllMemory();

						for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
						{
							delete (*it).second;

							it = atlas_part_map.erase(it);
						}

						return false;
					}

					tile_index = border * (final_tile_inc + mNumComponents);
					part_index = y * tile_size * part_inc +
						x * tile_inc;
					readable_tile_width = tile_inc;
					readable_tile_height = tile_size;

					if (0 != x)
					{
						tile_index -= border * mNumComponents;
						part_index -= border * mNumComponents;
						readable_tile_width += border * mNumComponents;
					}

					if (tiles_per_part - 1 != x)
					{
						readable_tile_width += border * mNumComponents;
					}

					if (0 != y)
					{
						tile_index -= border * final_tile_inc;
						part_index -= border * part_inc;
						readable_tile_height += border;
					}

					if (tiles_per_part - 1 != y)
					{
						readable_tile_height += border;
					}

					for (UINT32 y_in_tile= 0;
						y_in_tile < readable_tile_height; y_in_tile++)
					{
						memcpy( &single_tile[tile_index],
							&data_pointer[part_index], readable_tile_width );

						tile_index += final_tile_inc;
						part_index += part_inc;
					}

					if (0 == x)
					{
						// left border of current part

						if (0 == pX)
						{
							// left border of whole Atlas
							// copy left column into border zone
							// x == 0 and pX == 0

							tile_index = border * final_tile_inc;

							// the part is the tile itself!
							part_index = border *
								(final_tile_inc + mNumComponents);

							for (UINT32 y_in_tile= 0;
								y_in_tile < tile_size; y_in_tile++)
							{
								for (UINT32 b = 0; b < border; b++)
								{
									memcpy( &single_tile[tile_index + b * mNumComponents],
										&single_tile[part_index],
										mNumComponents );
								}
										
								tile_index += final_tile_inc;
								part_index += final_tile_inc;
							}

							// copy upper and lower left corner-pixels

							// upper left
							tile_index = 0;
							part_index = border *
								(final_tile_inc + mNumComponents);

							for (UINT32 y_in_tile= 0;
								y_in_tile < border; y_in_tile++)
							{
								for (UINT32 b = 0; b < border; b++)
								{
									memcpy( &single_tile[tile_index + b * mNumComponents],
										&single_tile[part_index],
										mNumComponents );
								}

								tile_index += final_tile_inc;
							}

							upper_left_copied = true;

							// lower left
							tile_index = (border + tile_size) * final_tile_inc;
							part_index = (border + tile_size - 1) * final_tile_inc
								+ border * mNumComponents;

							for (UINT32 y_in_tile= 0;
								y_in_tile < border; y_in_tile++)
							{
								for (UINT32 b = 0; b < border; b++)
								{
									memcpy( &single_tile[tile_index + b * mNumComponents],
										&single_tile[part_index],
										mNumComponents );
								}

								tile_index += final_tile_inc;
							}

							lower_left_copied = true;

						} else
						{
							// current part has left neighbor
							// copy the usable neighbor area as a whole

							// x == 0 and pX != 0

							data_pointer =
								GetPartDataPointer( scaled_dir_used,
								current_part_name,
								current_part_path, scaled_path,
								atlas_format, *mAtlas,
								atlas_part_map,
								times, pX - 1, pY );

							if (!data_pointer)
							{
								VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

								free( single_tile );
								free( merged_part );
								free( scaled_part );

								VTTMemoryManager::Get()->FreeAllMemory();

								for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
								{
									delete (*it).second;

									it = atlas_part_map.erase(it);
								}

								return false;
							}

							tile_index = border * final_tile_inc;
							part_index = y * tile_size * part_inc +
								part_inc - border * mNumComponents;
							readable_tile_width =
								border * mNumComponents;
							readable_tile_height = tile_size;

							if (0 != y)
							{
								tile_index -= border * final_tile_inc;
								part_index -= border * part_inc;
								readable_tile_height += border;
							}

							if (tiles_per_part - 1 != y)
							{
								readable_tile_height += border;
							}

							for (UINT32 y_in_tile= 0;
								y_in_tile < readable_tile_height;
								y_in_tile++)
							{
								memcpy( &single_tile[tile_index],
									&data_pointer[part_index],
									readable_tile_width );

								tile_index += final_tile_inc;
								part_index += part_inc;
							}

							if (0 == y && 0 != pY)
							{
								// current part has upper left neighbor
								// x == 0 and y == 0 and pX != 0 and pY != 0

								data_pointer =
									GetPartDataPointer( scaled_dir_used,
									current_part_name,
									current_part_path, scaled_path,
									atlas_format, *mAtlas,
									atlas_part_map,
									times, pX - 1, pY - 1 );

								if (!data_pointer)
								{
									VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

									free( single_tile );
									free( merged_part );
									free( scaled_part );

									VTTMemoryManager::Get()->FreeAllMemory();

									for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
									{
										delete (*it).second;

										it = atlas_part_map.erase(it);
									}

									return false;
								}

								tile_index = 0;
								part_index = (part_size - border + 1) * part_inc - border * mNumComponents;
								readable_tile_width =
									border * mNumComponents;
								readable_tile_height = border;

								for (UINT32 y_in_tile= 0;
									y_in_tile < readable_tile_height;
									y_in_tile++)
								{
									memcpy( &single_tile[tile_index],
										&data_pointer[part_index],
										readable_tile_width );

									tile_index += final_tile_inc;
									part_index += part_inc;
								}
							}

							if (tiles_per_part - 1 == y &&
								parts_per_side - 1 != pY)
							{
								// current part has lower left neighbor
								// x == 0 and y == tilesPerPart - 1 and pX != 0 and pY != partsPerSide - 1

								data_pointer =
									GetPartDataPointer( scaled_dir_used,
									current_part_name,
									current_part_path, scaled_path,
									atlas_format, *mAtlas,
									atlas_part_map,
									times, pX - 1, pY + 1 );

								if (!data_pointer)
								{
									VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

									free( single_tile );
									free( merged_part );
									free( scaled_part );

									VTTMemoryManager::Get()->FreeAllMemory();

									for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
									{
										delete (*it).second;

										it = atlas_part_map.erase(it);
									}

									return false;
								}

								tile_index = (final_tile_size - border) * final_tile_inc;
								part_index = part_inc - (border * mNumComponents);
								readable_tile_width =
									border * mNumComponents;
								readable_tile_height = border;

								for (UINT32 y_in_tile= 0;
									y_in_tile < readable_tile_height;
									y_in_tile++)
								{
									memcpy( &single_tile[tile_index],
										&data_pointer[part_index],
										readable_tile_width );

									tile_index += final_tile_inc;
									part_index += part_inc;
								}
							}
						}
					}
							
					// Yes, x can both be tiles_per_part - 1 and 0
					if (tiles_per_part - 1 == x)
					{
						// right border of current part
						if (parts_per_side - 1 == pX)
						{
							// right border of whole Atlas
							// copy right column into border zone
							// x == tilesPerPart - 1 and pX == partsPerSide - 1

							tile_index = ((border + 1) * final_tile_inc) -
								border * mNumComponents;

							// the part is the tile itself!
							part_index = ((border + 1) * final_tile_inc) -
								(border + 1) * mNumComponents;

							for (UINT32 y_in_tile= 0;
								y_in_tile < tile_size; y_in_tile++)
							{
								for (UINT32 b = 0; b < border; b++)
								{
									memcpy( &single_tile[tile_index + b * mNumComponents],
										&single_tile[part_index],
										mNumComponents );
								}

								tile_index += final_tile_inc;
								part_index += final_tile_inc;
							}

							// copy upper right and lower right
							// corner pixels

							// upper right
							tile_index = final_tile_inc -
								border * mNumComponents;
							part_index = (border + 1) * final_tile_inc
								- (border + 1) * mNumComponents;

							for (UINT32 y_in_tile= 0;
								y_in_tile < border; y_in_tile++)
							{
								for (UINT32 b = 0; b < border; b++)
								{
									memcpy( &single_tile[tile_index + b * mNumComponents],
										&single_tile[part_index],
										mNumComponents );
								}

								tile_index += final_tile_inc;
							}

							upper_right_copied = true;

							// lower right
							tile_index = (border + tile_size + 1) *
								final_tile_inc -
								border * mNumComponents;
							part_index = (border + tile_size) *
								final_tile_inc -
								(border + 1) * mNumComponents;

							for (UINT32 y_in_tile= 0;
								y_in_tile < border; y_in_tile++)
							{
								for (UINT32 b = 0; b < border; b++)
								{
									memcpy( &single_tile[tile_index + b * mNumComponents],
										&single_tile[part_index],
										mNumComponents );
								}

								tile_index += final_tile_inc;
							}

							lower_right_copied = true;
						} else
						{
							// current part has right neighbor

							// x == tilesPerPart - 1 and pX != partsPerSide - 1

							data_pointer =
								GetPartDataPointer( scaled_dir_used,
								current_part_name,
								current_part_path, scaled_path,
								atlas_format, *mAtlas,
								atlas_part_map,
								times, pX + 1, pY );

							if (!data_pointer)
							{
								VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

								free( single_tile );
								free( merged_part );
								free( scaled_part );

								VTTMemoryManager::Get()->FreeAllMemory();

								for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
								{
									delete (*it).second;

									it = atlas_part_map.erase(it);
								}

								return false;
							}

							tile_index = (border + 1) * final_tile_inc - border * mNumComponents;
							part_index = y * tile_size * part_inc;
							readable_tile_width =
								border * mNumComponents;
							readable_tile_height = tile_size;

							if (0 != y)
							{
								tile_index -= border * final_tile_inc;
								part_index -= border * part_inc;
								readable_tile_height += border;
							}

							if (tiles_per_part - 1 != y)
							{
								readable_tile_height += border;
							}

							for (UINT32 y_in_tile= 0;
								y_in_tile < readable_tile_height;
								y_in_tile++)
							{
								memcpy( &single_tile[tile_index],
									&data_pointer[part_index],
									readable_tile_width );

								tile_index += final_tile_inc;
								part_index += part_inc;
							}

							if (0 == y && 0 != pY)
							{
								// current part has upper right neighbor
								// x == tilesPerPart - 1 and y == 0 and pX != partsPerSide - 1 and pY != 0

								data_pointer =
									GetPartDataPointer( scaled_dir_used,
									current_part_name,
									current_part_path, scaled_path,
									atlas_format, *mAtlas,
									atlas_part_map,
									times, pX + 1, pY - 1 );

								if (!data_pointer)
								{
									VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

									free( single_tile );
									free( merged_part );
									free( scaled_part );

									VTTMemoryManager::Get()->FreeAllMemory();

									for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
									{
										delete (*it).second;

										it = atlas_part_map.erase(it);
									}

									return false;
								}

								tile_index = final_tile_inc - border * mNumComponents;
								part_index = (part_size - border) * part_inc;
								readable_tile_width =
									border * mNumComponents;
								readable_tile_height = border;

								for (UINT32 y_in_tile= 0;
									y_in_tile < readable_tile_height;
									y_in_tile++)
								{
									memcpy( &single_tile[tile_index],
										&data_pointer[part_index],
										readable_tile_width );

									tile_index += final_tile_inc;
									part_index += part_inc;
								}
							}

							if (tiles_per_part - 1 == y &&
								parts_per_side - 1 != pY)
							{
								// current part has lower right neighbor
								// x == tilesPerPart - 1 and y == tilesPerPart - 1 and pX != partsPerSide - 1 and pY != partsPerSide - 1)

								data_pointer =
									GetPartDataPointer( scaled_dir_used,
									current_part_name,
									current_part_path, scaled_path,
									atlas_format, *mAtlas,
									atlas_part_map,
									times, pX + 1, pY + 1 );

								if (!data_pointer)
								{
									VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

									free( single_tile );
									free( merged_part );
									free( scaled_part );

									VTTMemoryManager::Get()->FreeAllMemory();

									for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
									{
										delete (*it).second;

										it = atlas_part_map.erase(it);
									}

									return false;
								}

								tile_index = (final_tile_size - border + 1) * final_tile_inc - border * mNumComponents;
								part_index = 0;
								readable_tile_width =
									border * mNumComponents;
								readable_tile_height = border;

								for (UINT32 y_in_tile= 0;
									y_in_tile < readable_tile_height;
									y_in_tile++)
								{
									memcpy( &single_tile[tile_index],
										&data_pointer[part_index],
										readable_tile_width );

									tile_index += final_tile_inc;
									part_index += part_inc;
								}
							}
						}
					}

					if (0 == y)
					{
						// top border of current part
						if (0 == pY)
						{
							// top border of whole Atlas
							// copy top row into border zone
							// y == 0 and pY == 0

							tile_index = border * mNumComponents;

							// the part is the tile itself!
							part_index = border * 
								(final_tile_inc + mNumComponents);

							for (UINT32 b = 0; b < border; b++)
							{
								memcpy( &single_tile[tile_index],
									&single_tile[part_index],
									tile_size * mNumComponents );

								tile_index += final_tile_inc;
							}

							// copy upper left and upper right
							// corner-pixels

							// upper left
							if (!upper_left_copied)
							{
								tile_index = 0;
								part_index = border *
									(final_tile_inc + mNumComponents);

								for (UINT32 y_in_tile= 0;
									y_in_tile < border; y_in_tile++)
								{
									for (UINT32 b = 0; b < border; b++)
									{
										memcpy( &single_tile[tile_index +
											b * mNumComponents],
											&single_tile[part_index],
											mNumComponents );
									}

									tile_index += final_tile_inc;
								}
							}

							// upper right
							if (!upper_right_copied)
							{
								tile_index = final_tile_inc -
									border * mNumComponents;
								part_index = (border + 1) * final_tile_inc
									- (border + 1) * mNumComponents;

								for (UINT32 y_in_tile= 0;
									y_in_tile < border; y_in_tile++)
								{
									for (UINT32 b = 0; b < border; b++)
									{
										memcpy( &single_tile[tile_index + b * mNumComponents],
											&single_tile[part_index],
											mNumComponents );
									}

									tile_index += final_tile_inc;
								}
							}

						} else
						{
							// current part has upper neighbor

							// y == 0 and pY != 0

							data_pointer =
								GetPartDataPointer( scaled_dir_used,
									current_part_name,
									current_part_path, scaled_path,
									atlas_format, *mAtlas,
									atlas_part_map,
									times, pX, pY - 1 );

							if (!data_pointer)
							{
								VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

								free( single_tile );
								free( merged_part );
								free( scaled_part );

								VTTMemoryManager::Get()->FreeAllMemory();

								for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
								{
									delete (*it).second;

									it = atlas_part_map.erase(it);
								}

								return false;
							}
									
							tile_index = border * mNumComponents;
							part_index = (part_size - border) *
								part_inc + x * tile_inc;
							readable_tile_width = tile_inc;
							readable_tile_height = border;

							if (0 != x)
							{
								tile_index -= border * mNumComponents;
								part_index -= border * mNumComponents;
								readable_tile_width += border * mNumComponents;
							}

							if (tiles_per_part - 1 != x)
							{
								readable_tile_width += border * mNumComponents;
							}

							for (UINT32 y_in_tile= 0;
								y_in_tile < readable_tile_height;
								y_in_tile++)
							{
								memcpy( &single_tile[tile_index],
									&data_pointer[part_index],
									readable_tile_width );

								tile_index += final_tile_inc;
								part_index += part_inc;
							}
						}
					}
							
					// Yes, y can both be tiles_per_part - 1 and 0
					if (tiles_per_part - 1 == y)
					{
						// lower border of current part
						if (parts_per_side - 1 == pY)
						{
							// lower border of whole Atlas
							// copy lower row into border zone
							// y == tilesPerPart - 1 and pY == partsPerSide - 1

							tile_index = (border + tile_size) * final_tile_inc +
								border * mNumComponents;

							// the part is the tile itself!
							part_index = (border + tile_size - 1) * final_tile_inc +
								border * mNumComponents;

							for (UINT32 b = 0; b < border; b++)
							{
								memcpy( &single_tile[tile_index],
									&single_tile[part_index],
									tile_size * mNumComponents );

								tile_index += final_tile_inc;
							}

							// copy lower left and lower right
							// corner pixels

							// lower left
							if (!lower_left_copied)
							{
								tile_index = (border + tile_size) * final_tile_inc;
								part_index = (border + tile_size - 1) * final_tile_inc
									+ border * mNumComponents;

								for (UINT32 y_in_tile= 0;
									y_in_tile < border; y_in_tile++)
								{
									for (UINT32 b = 0; b < border; b++)
									{
										memcpy( &single_tile[tile_index + b * mNumComponents],
											&single_tile[part_index],
											mNumComponents );
									}

									tile_index += final_tile_inc;
								}
							}

							// lower right
							if (!lower_right_copied)
							{
								tile_index = (border + tile_size + 1) *
									final_tile_inc -
									border * mNumComponents;
								part_index = (border + tile_size) *
									final_tile_inc -
									(border + 1) * mNumComponents;

								for (UINT32 y_in_tile= 0;
									y_in_tile < border; y_in_tile++)
								{
									for (UINT32 b = 0; b < border; b++)
									{
										memcpy( &single_tile[tile_index + b * mNumComponents],
											&single_tile[part_index],
											mNumComponents );
									}

									tile_index += final_tile_inc;
								}
							}
									
						} else
						{
							// current part has lower neighbor
							// y == tilesPerPart - 1 and pY != partsPerSide - 1

							data_pointer =
								GetPartDataPointer( scaled_dir_used,
								current_part_name,
								current_part_path, scaled_path,
								atlas_format, *mAtlas,
								atlas_part_map,
								times, pX, pY + 1 );

							if (!data_pointer)
							{
								VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Received null-pointer for input Atlas part" );

								free( single_tile );
								free( merged_part );
								free( scaled_part );

								VTTMemoryManager::Get()->FreeAllMemory();

								for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
								{
									delete (*it).second;

									it = atlas_part_map.erase(it);
								}

								return false;
							}

							tile_index = (final_tile_size - border) *
								final_tile_inc +
								border * mNumComponents;
							part_index = x * tile_inc;
							readable_tile_width = tile_inc;
							readable_tile_height = border;

							if (0 != x)
							{
								tile_index -= border * mNumComponents;
								part_index -= border * mNumComponents;
								readable_tile_width += border * mNumComponents;
							}

							if (tiles_per_part - 1 != x)
							{
								readable_tile_width += border * mNumComponents;
							}

							for (UINT32 y_in_tile= 0;
								y_in_tile < readable_tile_height;
								y_in_tile++)
							{
								memcpy( &single_tile[tile_index],
									&data_pointer[part_index],
									readable_tile_width );

								tile_index += final_tile_inc;
								part_index += part_inc;
							}
						}
					}
				}

				if (!VTTImage::WriteImageFile( tile_path,
					single_tile, final_tile_size, final_tile_size,
					mNumComponents,
					mAtlas->GetConfiguration().GetPictureQuality() ))
				{

					VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::DoIt(): Could not write single tile " + tile_path );

					free( single_tile );
					free( merged_part );
					free( scaled_part );

					VTTMemoryManager::Get()->FreeAllMemory();

					for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
					{
						delete (*it).second;

						it = atlas_part_map.erase(it);
					}

					return false;
				}
			}
			//}
		}
		//}

		map_helper.clear();

		for (tile_it = tilesToConsider.begin(); tile_it != tilesToConsider.end(); tile_it++)
		{
			helper_it = map_helper.insert( pair<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>>( tile_it->first, set<std::tr1::array<UINT32, 2>>() ) ).first;

			for (set_it = tile_it->second.begin(); set_it != tile_it->second.end(); set_it++)
			{
				element[0] = (*set_it)[0] / 2;
				element[1] = (*set_it)[1] / 2;

				helper_it->second.insert( element );
			}
		}

		tilesToConsider.clear();
		tilesToConsider = map_helper;

		cout << endl;
		times += 1;
		size /= 2;
	}

	free( single_tile );
	free( merged_part );
	free( scaled_part );

	VTTMemoryManager::Get()->FreeAllMemory();

	for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
	{
		delete (*it).second;

		it = atlas_part_map.erase(it);
	}

	return true;
}

/*
void VTTTileStore::CancelAtlasAndTileStoreGeneration( void )
{
	if (mIsWritingAtlasAndTileStore && mWriteAtlasAndTileStoreThread.joinable())
	{
		mWriteAtlasAndTileStoreThread.interrupt();
		mWriteAtlasAndTileStoreThread.join();
		mIsWritingAtlasAndTileStore = false;
	}
}
*/

/**
  * Returns a vector with VTTTileInfo objects, that include the information about
  * all the tiles that have been updated and have to be streamed again to the GPU.
  *
  * @return vector with VTTTileInfo objects, that include the information about all the tiles that have been updated
  */
const std::vector<VTTTileInfo> &VTTTileStore::FetchUpdateResults( void ) const
{
	return mUpdateResults;
}

/**
  * Returns a boolean value indicating whether the last update procedure was successful or not
  *
  * @return boolean value indicating whether the last update procedure was successful or not
  */
bool VTTTileStore::GetUpdateResult( void ) const
{
	return mUpdateResult;
}

/**
  * Returns a boolean value indicating whether the last write procedure was successful or not
  *
  * @return boolean value indicating whether the last write procedure was successful or not
  */
bool VTTTileStore::GetWriteResult( void ) const
{
	return mWriteResult;
}

/**
  * Returns a boolean value indicating whether the Tile Store is currently updated or not
  *
  * @return boolean value indicating whether the Tile Store is currently updated or not
  */
bool VTTTileStore::IsUpdating( void ) const
{
	return mIsUpdating;
}

/**
  * Returns a boolean value indicating whether the Tile Store is currently written to hard disc or not
  *
  * @return boolean value indicating whether the Tile Store is currently written to hard disc or not
  */
bool VTTTileStore::IsWriting( void ) const
{
	return mIsWriting;
}

/**
  *	Updates the Tile Store in a separate thread. The method returns therefore immediately and calls
  * the callbackFunction when the update is ready.
  *
  * @param callbackFunction pointer to a function that is called when the update is ready. The parameter is true when the update was successful, otherwise false
  */
void VTTTileStore::UpdateInSeparateThread( void (*callbackFunction)(bool) )
{
	//cout << "tileStore: " << this << endl;

	if (mIsUpdating || mUpdateThread.joinable())
	{
		VTTErrorManager::Get()->pushErrorMessage("VTTTileStore::UpdateInSeparateThread(void (*)(bool)): Already updating Tile Store");
		callbackFunction( false );
	}
	else
	{
		// cout << "UpdateInSeparateThread tilesStore = " << this << endl;
		mUpdateThread = boost::thread( &VTTTileStore::UpdateThread, this, callbackFunction );
	}
}

/**
  *	The method that is called by the update-thread to update the Tile Store.
  *
  * @param tileStore pointer to the Tile Store that is updated
  * @param callbackFunction pointer to a function that is called when the update is ready. The parameter is true when the update was successful, otherwise false
  */
void VTTTileStore::UpdateThread( VTTTileStore *tileStore, void (*callbackFunction)(bool) )
{
	cout << "UpdateThread tilesStore = " << tileStore << endl;
	tileStore->Update( callbackFunction );
}

/**
  *	Evaluates all the tiles on the hard disc, that correspond to changed Atlas parts. These tiles are then updated.
  *
  * @param callbackFunction pointer to a function that is called when the update is ready. The parameter is true when the update was successful, otherwise false
  */
void VTTTileStore::Update( void (*callbackFunction)(bool) )
{
	mIsUpdating = true;

	if (IsNull())
	{
		//cout << "ERROR: Can not update Null-TileStore" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Can not update Null-TileStore " );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	if (!IsDir( mAtlas->GetFullPath() ))
	{
		//cout << "ERROR: Atlas directory does not exist" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Atlas directory " + mAtlas->GetFullPath() + " does not exist " );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	if (!IsDir( mFullPath ))
	{
		//cout << "ERROR: Tile Store directory does not exist" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Tile Store directory " + mFullPath + " does not exist " );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	if (!IsDir( mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "scaled" ))
	{
		//cout << "ERROR: scaled directory does not exist" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): scaled directory " + mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "scaled" + " does not exist " );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	if (!IsFile( mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "textureAtlasInfo.txt" ))
	{
		//cout << "ERROR: textureAtlasInfo.txt could not be found in Texture Atlas directory" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): textureAtlasInfo.txt could not be found in Texture Atlas directory " );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	VTTInfoFile info_file( mAtlas->GetFullPath() +
		VTT_FILE_SEPARATOR + "textureAtlasInfo.txt" );

	if (info_file.IsNull())
	{
		//cout << "ERROR: Can not update with invalid textureAtlasInfo.txt"
		//	 << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Can not update with invalid textureAtlasInfo.txt " );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	string scaled_path = mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "scaled";
	string atlas_format =
		GetFileFormatEnding( mAtlas->GetConfiguration().GetAtlasFormat() );
	string tiles_format =
		GetFileFormatEnding( mAtlas->GetConfiguration().GetTilesFormat() );

	map<string, VTTImage*> atlas_part_map;
	map<string, VTTImage*>::iterator it;

	UINT32 num_elements_in_part = (mAtlas->GetPartSideLength() *
		mAtlas->GetPartSideLength()) * mNumComponents;

	unsigned char *single_part = static_cast<unsigned char *>(
		malloc( num_elements_in_part ) );

	if (!single_part)
	{
		//cout << "ERROR: Not enough memory to update the Tile Store"
		//	 << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Not enough memory to update the Tile Store " );
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	const map<string, VTTImageInfo> &info_map = info_file.GetNamesToInfoMap();
	vector<string> changed_images;

	// Find the names of the images, that have changed
	for (map<string, VTTImageInfo>::const_iterator it = info_map.begin(); it != info_map.end(); it++)
	{
		if (IsFile( mAtlas->GetConfiguration().GetBasePath() + VTT_FILE_SEPARATOR + (*it).first ))
		{
			//cout << "INFO: OK, " << (*it).first << " is here" << endl;

			if (!((*it).second.changeTime == (*mAtlas->GetNamesToImagesMap().find( (*it).first )->second->UpdateLastChangingDate())))
			{
				changed_images.push_back( (*it).first );
			}

		} else
		{
			//cout << "ERROR: Image " << (*it).first << " present in the Texture Atlas is missing"
			//	 << endl;
			VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Image " + (*it).first + " present in the Texture Atlas is missing" );

			free( single_part );
			//return false;
			mIsUpdating = false;
			mUpdateResult = false;
			if (callbackFunction != 0)
				callbackFunction( false );
			return;
		}
	}

	if (0 == changed_images.size())
	{
		//cout << "INFO: No changes in the images" << endl;
		free( single_part );
		//return true;
		mIsUpdating = false;
		mUpdateResult = true;
		if (callbackFunction != 0)
			callbackFunction( true );
		return;
	}

	//cout << "\n- Changed images:";
 	//for (vector<string>::iterator it = changed_images.begin(); it != changed_images.end(); it++)
 	//	cout << "\n-\t" << (*it) << endl;

	//cout << endl;

	map<std::tr1::array<UINT32, 2>, set<string>> changed_parts;
	map<std::tr1::array<UINT32, 2>, set<string>>::iterator map_it;

	const UINT32 *area;
	UINT32 border = mAtlas->GetConfiguration().GetBorder();
	UINT32 final_tile_size = mAtlas->GetConfiguration().GetTileSize();
	UINT32 part_size = mAtlas->GetPartSideLength();
	UINT32 parts_per_side = mAtlas->GetPartsPerSide();
	UINT32 tile_size = final_tile_size - 2 * border;

	std::tr1::array<UINT32, 2> coordinate;

	const vector<std::tr1::array<UINT32, 4>> *current_areas = 0;

	// Find all atlas parts, which correspond to changed images
	for (vector<string>::iterator it = changed_images.begin(); it != changed_images.end(); it++)
	{
		current_areas = &info_map.find( *it )->second.areas;

		for (vector<std::tr1::array<UINT32, 4>>::const_iterator area_it = current_areas->begin();
			area_it != current_areas->end(); area_it++)
		{
			area = &((*area_it)[0]);

			for (UINT32 y = area[1] / part_size; y <= (area[3] - 1) / part_size; y++)
			{
				coordinate[1] = y;

				for (UINT32 x = area[0] / part_size; x <= (area[2] - 1) / part_size; x++)
				{
 					coordinate[0] = x;

					// 				map_it = changed_parts.find( coordinate );
					// 
					// 				if (changed_parts.end() == map_it)
					// 				{
					map_it = changed_parts.insert( pair<std::tr1::array<UINT32, 2>, set<string>>( coordinate, set<string>() ) ).first;
					// 				}

 					map_it->second.insert( *it );
 				}
			}
		}
	}

	// changed_tiles maps atlas part coordinates (x, y) to the internal coordinates of changed tiles

	map<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>> changed_tiles;
	map<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>>::iterator ch_ti_it;

	cout << "INFO: Updating Atlas..." << endl;

	UINT32 x = 0;
	UINT32 x_min = 0;
	UINT32 x_max = 0;
	INT32 x_coord = 0;

	UINT32 y = 0;
	UINT32 y_min = 0;
	UINT32 y_max = 0;
	INT32 y_coord = 0;

	UINT32 atlas_part_index = 0;
	UINT32 atlas_part_line_inc = mAtlas->GetPartSideLength() * mNumComponents;
	UINT32 image_inc = 0;
	UINT32 image_index = 0;

	UINT32 line_length = 0;

	UINT32 sub_image_width = 0;
	UINT32 sub_image_height = 0;

	UINT32 sub_y_start = 0;
	UINT32 sub_y_end = 0;

	UINT32 sub_x_start = 0;
	UINT32 sub_x_end = 0;

	UINT32 out_rect[4];

	std::tr1::array<UINT32, 2> tile_coord;

	string current_part_name;
	string current_part_path;

	const unsigned char *data_pointer;

	VTTImage *current_image;

	string atlas_part_name;

	// Update the Texture Atlas and find the tiles, which correspond to changed images
	for (map_it = changed_parts.begin(); map_it != changed_parts.end(); map_it++)
	{
		x = map_it->first[0];
		y = map_it->first[1];

		atlas_part_name = mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "_texture_atlas_" + UINT32ToString( x ) + "_" + UINT32ToString( y ) + atlas_format;

		// Load the part, that has to be updated
		data_pointer = GetPartDataPointer( false, current_part_name, current_part_path, "", atlas_format, *mAtlas, atlas_part_map, 0, x, y );

		if (!data_pointer)
		{
			//cout << "ERROR: Could not load atlas part " << current_part_name
			VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Received null-pointer for Atlas part input image " + current_part_name );
			//	 << endl;
			free( single_part );
			//return false;
			mIsUpdating = false;
			mUpdateResult = false;
			if (callbackFunction != 0)
				callbackFunction( false );
			return;
		}

		// Use the old part as the base for the new one
		memcpy( single_part, data_pointer, num_elements_in_part );

		out_rect[0] = x * part_size;
		out_rect[1] = y * part_size;
		out_rect[2] = out_rect[0] + part_size;
		out_rect[3] = out_rect[1] + part_size;

		for (set<string>::iterator str_it = map_it->second.begin(); str_it != map_it->second.end(); str_it++)
		{
			current_image = mAtlas->GetNamesToImagesMap().find( *str_it )->second;
			
			// Reload changed image
			if (!current_image->Reload())
			{
				//cout << "ERROR: Could not reload image " << *str_it
				VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Could not reload image " + *str_it );
				//	 << endl;

				free( single_part );

				//return false;
				mIsUpdating = false;
				mUpdateResult = false;
				if (callbackFunction != 0)
					callbackFunction( false );
				return;
			}

			data_pointer = current_image->GetDataPointer();

			if (!data_pointer)
			{
				//cout << "ERROR: Could not load atlas part " << current_part_name
				VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Received null-pointer for input image " + *str_it );
				//	 << endl;
				free( single_part );
				//return false;
				mIsUpdating = false;
				mUpdateResult = false;
				if (callbackFunction != 0)
					callbackFunction( false );
				return;
			}

			current_areas = &info_map.find( *str_it )->second.areas;
			
			for (vector<std::tr1::array<UINT32, 4>>::const_iterator area_it = current_areas->begin();
				area_it != current_areas->end(); area_it++)
			{
				area = &(*area_it)[0];

				// Check if area intersects with current atlas part)
				if (area[0] >= out_rect[2] || area[2] <= out_rect[0] || area[1] >= out_rect[3] || area[3] <= out_rect[1])
					continue;

				// Upper left coordinate of the image in part-space
				x_coord = area[0] - out_rect[0];
				y_coord = area[1] - out_rect[1];

				// Internal coordinates of the area that has changed
				x_min = max( x_coord, 0 );
				y_min = max( y_coord, 0 );
				x_max = min( area[2] - out_rect[0], part_size );
				y_max = min( area[3] - out_rect[1], part_size );

				// Copy the changed image-area into the updated part

				sub_image_width = min( area[2], out_rect[2] ) - max( area[0], out_rect[0] );
				sub_image_height = min( area[3], out_rect[3] ) - max( area[1], out_rect[1] );

				sub_y_start = y_coord < 0 ? 0 : y_coord;
				sub_y_end = sub_y_start + sub_image_height;

				sub_x_start = x_coord < 0 ? 0 : x_coord;
				sub_x_end = sub_x_start + sub_image_width;

				atlas_part_index = (sub_y_start * part_size + sub_x_start) * mNumComponents;
				atlas_part_line_inc = part_size * mNumComponents;

				image_index = image_inc * (y_coord < 0 ? abs( y_coord ) : 0) +
					(x_coord < 0 ? abs( x_coord ) : 0) * mNumComponents;

				image_inc = current_image->GetWidth() * current_image->GetNumComponents();

				line_length = sub_image_width * mNumComponents;

				// Fill the atlas_part with the (Sub-)Image
				for (UINT32 y_in_sub = 0; y_in_sub < sub_image_height; y_in_sub++)
				{
					memcpy( &single_part[atlas_part_index], &data_pointer[image_index], line_length );

					atlas_part_index += atlas_part_line_inc;
					image_index += image_inc;
				}

				for (UINT32 t_y = y_min / tile_size; t_y <= (y_max - 1u) / tile_size; t_y++)
				{
					for (UINT32 t_x = x_min / tile_size; t_x <= (x_max - 1u) / tile_size; t_x++)
					{
						tile_coord[0] = t_x;
						tile_coord[1] = t_y;

						// 					ch_ti_it = changed_tiles.find( map_it->first );
						// 
						// 					if (changed_tiles.end() == ch_ti_it)
						// 					{
						ch_ti_it = changed_tiles.insert( pair<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>>( map_it->first, set<std::tr1::array<UINT32, 2>>() ) ).first;
						// 					}

						ch_ti_it->second.insert( tile_coord );
					}
				}
			}
		}

		// Write the changed part to disc

		if (!VTTImage::WriteImageFile( atlas_part_name,
			single_part, part_size, part_size, mNumComponents, mAtlas->GetConfiguration().GetPictureQuality() ))
		{
			//cout << "ERROR: Could not load atlas part " << current_part_name
			VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Could not write updated Atlas part " + atlas_part_name );
			//	 << endl;
			free( single_part );
			//return false;
			mIsUpdating = false;
			mUpdateResult = false;
			if (callbackFunction != 0)
				callbackFunction( false );
			return;
		}
 	}

	free( single_part );

	cout << "INFO: Updating Tile Store..." << endl << endl;

	bool result = DoIt( true, true, changed_tiles );

	VTTMemoryManager::Get()->FreeAllMemory();

	for (it = atlas_part_map.begin(); it != atlas_part_map.end();)
	{
		delete (*it).second;

		it = atlas_part_map.erase(it);
	}

	if (!result)
	{
		//cout << "ERROR: Tile Store and Atlas update failed!" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Tile Store and Atlas update failed" );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	cout << endl << "INFO: Atlas and Tile Store successfully updated!" << endl;

	// Update the Info-File

	vector<string>::iterator str_it;

	for (str_it = changed_images.begin(); str_it != changed_images.end(); str_it++)
	{
		if (!info_file.SetChangeTimeForImageName( *str_it, *(mAtlas->GetNamesToImagesMap().find( *str_it )->second->UpdateLastChangingDate()) ))
		{
			//cout << "ERROR: Could not update Atlas Info File" << endl;
			VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Could not update Atlas Info File" );
			//return false;
			mIsUpdating = false;
			mUpdateResult = false;
			if (callbackFunction != 0)
				callbackFunction( false );
			return;
		}
	}

	if (!info_file.WriteToFile())
	{
		//cout << "ERROR: Could not update Atlas Info File" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Update(): Could not update Atlas Info File" );
		//return false;
		mIsUpdating = false;
		mUpdateResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	//cout << endl << "INFO: Tile Store and Atlas successfully updated" << endl;

	//return true;
	mUpdateResult = true;
	mIsUpdating = false;
	if (callbackFunction != 0)
		callbackFunction( true );
}

/**
  *	Writes the Tile Store in a separate thread. The method returns therefore immediately.
  * The callbackFunction is called after the write procedure.
  *
  * @param overwrite boolean value indicating whether the already existing files should be overwritten or not
  * @param callbackFunction pointer to a function that is called when the generation of the Tile Store is completed. The parameter is true when the generation was successful, otherwise false
  */
void VTTTileStore::WriteInSeparateThread( bool overwrite, void (*callbackFunction)(bool) )
{
	//cout << "tileStore: " << this << endl;

	if (mIsWriting || mWriteThread.joinable())
	{
		VTTErrorManager::Get()->pushErrorMessage("ERROR: VTTTileStore::WriteInSeparateThread(bool, void (*)(bool)): Already writing Tile Store");
		if (callbackFunction != 0)
			callbackFunction( false );
	}
	else
	{
		mWriteThread = boost::thread( &VTTTileStore::WriteThread, this, overwrite, callbackFunction );
	}
}

/**
  *	The method that is called by the writeThread to write the Tile Store to hard disc.
  *
  * @param tileStore pointer to the Tile Store that is written to hard disc
  * @param overwrite boolean value indicating whether already existing files should be overwritten (true) or not (false)
  * @param callbackFunction pointer to a function that is called when the generation of the Tile Store is completed. The parameter is true when the generation was successful, otherwise false
  */
void VTTTileStore::WriteThread( VTTTileStore *tileStore, bool overwrite, void (*callbackFunction)(bool) )
{
	tileStore->Write( overwrite, callbackFunction );
}

/**
  *	Writes the Tile Store to hard disc.
  *
  * @param overwrite boolean value indicating whether already existing files should be overwritten (true) or not (false)
  * @param callbackFunction pointer to a function that is called when the generation of the Tile Store is completed. The parameter is true when the generation was successful, otherwise false
  */
void VTTTileStore::Write( bool overwrite, void (*callbackFunction)(bool) )
{
	if (mIsWriting)
	{
		VTTErrorManager::Get()->pushErrorMessage("ERROR: VTTTileStore::WriteInSeparateThread(bool, void (*)(bool)): Already writing Tile Store");
		
		if (callbackFunction != 0)
			callbackFunction( false );

		return;
	}

	//cout << "Write: this = " << this << endl;
	mIsWriting = true;

	if (IsNull())
	{
		//cout << "ERROR: Can not write Null-TileStore" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Write(): Can not write Null-TileStore " );
		//return false;
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	if (!IsDir( mAtlas->GetFullPath() ))
	{
		//cout << "ERROR: Atlas directory does not exist" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Write(): Atlas directory " + mAtlas->GetFullPath() + " does not exist " );
		//return false;
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	if (!IsDir( mFullPath ) && !MakeDir( mFullPath ))
	{
		//cout << "ERROR: Could not create dir " << mFullPath << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Write(): Could not create Tile Store directory " + mFullPath );
		//return false;
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	string scaled_path = mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "scaled";

	if (!IsDir( scaled_path ) && !MakeDir( scaled_path ))
	{
		//cout << "ERROR: Could not create dir " << scaled_path << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTTileStore::Write(): Could not create scaled directory " + mAtlas->GetFullPath() + VTT_FILE_SEPARATOR + "scaled" );
		//return false;
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	string atlas_format =
		GetFileFormatEnding( mAtlas->GetConfiguration().GetAtlasFormat() );
	string tiles_format =
		GetFileFormatEnding( mAtlas->GetConfiguration().GetTilesFormat() );

	string atlas_part_path;

	// Check if all needed atlas parts are available
	for (UINT32 y = 0; y < mAtlas->GetPartsPerSide(); y++)
	{
		for (UINT32 x = 0; x < mAtlas->GetPartsPerSide(); x++)
		{

			atlas_part_path = mAtlas->GetFullPath() + VTT_FILE_SEPARATOR +
				"_texture_atlas_" + UINT32ToString( x ) + "_" +
				UINT32ToString( y ) + atlas_format;

			if (!IsFile( atlas_part_path ))
			{
				//cout << "ERROR: Missing atlas part " << atlas_part_path << endl;
				VTTErrorManager::Get()->pushErrorMessage( "Missing atlas part " + atlas_part_path );
				//return false;
				mIsWriting = false;
				mWriteResult = false;
				if (callbackFunction != 0)
					callbackFunction( false );
				return;
			}
		}
	}

	cout << "INFO: Found all atlas parts, start generation of the Tile Store..."
		 << endl << endl;

	// OK, everything is here, start generation
	std::tr1::array<UINT32, 2> coord;
	set<std::tr1::array<UINT32, 2>> all_tile_coords;
	map<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>> tiles_to_consider;

	UINT32 tiles_per_part =
		mAtlas->GetPartSideLength() /
			(mAtlas->GetConfiguration().GetTileSize() -
			2 * mAtlas->GetConfiguration().GetBorder());
	UINT32 parts_per_side = mAtlas->GetPartsPerSide();

	for (UINT32 y = 0; y < tiles_per_part; y++)
	{
		coord[1] = y;

		for (UINT32 x = 0; x < tiles_per_part; x++)
		{
			coord[0] = x;

			all_tile_coords.insert( coord );
		}
	}

	for (UINT32 y = 0; y < parts_per_side; y++)
	{
		coord[1] = y;

		for (UINT32 x = 0; x < parts_per_side; x++)
		{
			coord[0] = x;

			tiles_to_consider.insert( pair<std::tr1::array<UINT32, 2>, set<std::tr1::array<UINT32, 2>>>( coord, all_tile_coords ) );
		}
	}

	if (!DoIt( overwrite, false, tiles_to_consider ))
	{
		VTTErrorManager::Get()->pushErrorMessage( "VTTErrorManager::Write(bool): Tile Store generation failed" );
		//return false;
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	cout << endl << "INFO: Tile Store generated" << endl;

	//return true;
	if (callbackFunction != 0)
		callbackFunction( true );

	mWriteResult = true;
	mIsWriting = false;
}

/**
  *	Returns a pointer to the VTTAtlas object, the Tile Store is based on.
  *
  * @return pointer to the VTTAtlas object, the Tile Store is based on
  */
VTTAtlas *VTTTileStore::GetAtlas( void )
{
	return mAtlas;
}

/**
  *	Writes the Atlas, the Tile Store is based on, and the Tile Store itself to hard disc.
  *
  * @param overwrite boolean value indicating whether already existing files should be overwritten (true) or not (false)
  * @param callbackFunction pointer to a function that is called when the generation of the Atlas and the Tile Store is completed. The parameter is true when the generation was successful, otherwise false
  */
void VTTTileStore::WriteAtlasAndTileStoreInSeparateThread( bool overwrite, void (*callbackFunction)(bool) )
{
	if (mIsWritingAtlasAndTileStore || mWriteAtlasAndTileStoreThread.joinable() || mAtlas->IsWriting() || mIsWriting || mWriteThread.joinable())
	{
		VTTErrorManager::Get()->pushErrorMessage("VTTTileStore::WriteInSeparateThread(bool, void (*)(bool)): Already writing Atlas or Tile Store");
		
		if (callbackFunction != 0)
			callbackFunction( false );
	}
	else
	{
		mWriteAtlasAndTileStoreThread = boost::thread( &VTTTileStore::WriteAtlasAndTileStoreThread, this, overwrite, callbackFunction );
	}
}

/**
  *	Sets a flag, that is used as an indicator whether the Atlas and the Tile Store are currently written to hard disc or not.
  *
  * @param value boolean value indicating whether the Atlas and the Tile Store are currently written to hard disc (true) or not (false)
  */
void VTTTileStore::SetIsWritingAtlasAndTileStore( bool value )
{
	mIsWritingAtlasAndTileStore = value;
}

/**
  *	Method that is called by the writeAtlasAndTileStoreThread to generate the Atlas and the Tile Store.
  *
  * @param tileStore pointer to the Tile Store that is written to hard disc. The Atlas is written too
  * @param overwrite boolean value indicating whether already existing files should be overwritten (true) or not (false)
  * @param callbackFunction pointer to a function that is called when the generation of the Atlas and the Tile Store is completed. The parameter is true when the generation was successful, otherwise false
  */
void VTTTileStore::WriteAtlasAndTileStoreThread( VTTTileStore *tileStore, bool overwrite, void (*callbackFunction)(bool) )
{
	tileStore->SetIsWritingAtlasAndTileStore( true );
	tileStore->GetAtlas()->Write();

	if (!tileStore->GetAtlas()->GetWriteResult())
	{
		tileStore->SetIsWritingAtlasAndTileStore( false );
		callbackFunction( false );
		return;
	}

	if (!tileStore->GetAtlas()->WriteOffsetFile())
	{
		tileStore->SetIsWritingAtlasAndTileStore( false );
		callbackFunction( false );
		return;
	}

	tileStore->Write();

	tileStore->SetIsWritingAtlasAndTileStore( false );

	if (tileStore->GetWriteResult())
		callbackFunction( true );
	else
		callbackFunction( false );
}

/**
  *	Returns a gaussian filter kernel with the given width, height and sigma (root mean square deviation).
  * The kernel is written into the parameter kernel, which must provide enough space.
  * The kernel is written line by line.
  *
  * @param width width of the kernel in pixels
  * @param height height of the kernel in pixels
  * @param sigma root mean square deviation of the gaussian function
  * @param kernel buffer for the kernel values, that are written line by line into this buffer
  */
void GetGaussianFilterKernel( const UINT32 &width,
	const UINT32 &height, const float &sigma, float *kernel )
{
	if (0 == width || 0 == height || 0.0f >= sigma || !kernel)
		return;

	const float two_sigma_squared = 2.0f * sigma * sigma;

	float x_offset_start = -(width * 0.5f - 0.5f);
	float x_offset;
	float y_offset = -(height * 0.5f - 0.5f);

	float sum = 0.0f;

	UINT32 current_index = 0;

	for (UINT32 row = 0; row < height; row++)
	{
		x_offset = x_offset_start;
		for (UINT32 col = 0; col < width; col++)
		{
			kernel[current_index] =
				exp( -(x_offset * x_offset + y_offset * y_offset) / two_sigma_squared );

			sum += kernel[current_index];

			x_offset += 1.0f;
			current_index++;
		}

		y_offset += 1.0f;
	}

	// Normalize

	current_index = 0;

	for (UINT32 row = 0; row < height; row++)
	{
		for (UINT32 col = 0; col < width; col++)
		{
			kernel[current_index] /= sum;
			current_index++;
		}
	}
}

/**
  *	Returns a pointer to the raw image data of a particular Atlas part.
  *
  * @param scaledDirUsed boolean value indicating whether the considered Atlas part is inside the "scaled" subdirectory (true) or not (false)
  * @param currentPartName reference to an std::string, in which the calculated part name is stored
  * @param currentPartPath reference to an std::string, in which the calculated part path is stored
  * @param scaledPath path of the "scaled" subdirectory, which is used to store all the scaled Atlas parts
  * @param fileFormat file format ending of the Atlas, including the '.', e.g. ".jpg"
  * @param atlas const reference to the Atlas object
  * @param atlasPartMap map, that maps part names to VTTImage object pointers. The current part is added if it is not in the map
  * @param times current iteration number, equal to the current mip map level
  * @param pX x-coordinate of the Atlas part in the current mip map level
  * @param pY y-coordinate of the Atlas part in the current mip map level
  */
const unsigned char *GetPartDataPointer( const bool &scaledDirUsed,
	string &currentPartName, string &currentPartPath, const string &scaledPath,
	const string &fileFormat,
	const VTTAtlas &atlas, map<string, VTTImage*> &atlasPartMap,
	const UINT32 &times, const UINT32 &pX, const UINT32 &pY)
{
	if (scaledDirUsed)
	{
		currentPartName = "_texture_atlas_" +
			UINT32ToString( times ) + "_" +
			UINT32ToString( pX ) + "_" +
			UINT32ToString( pY ) + fileFormat;

		currentPartPath = scaledPath +
			VTT_FILE_SEPARATOR + currentPartName;
	} else
	{
		currentPartName = "_texture_atlas_" +
			UINT32ToString( pX ) + "_" +
			UINT32ToString( pY ) + fileFormat;
		currentPartPath = atlas.GetFullPath() +
			VTT_FILE_SEPARATOR + currentPartName;
	}

	map<string, VTTImage*>::iterator it = atlasPartMap.find( currentPartName );

	if (atlasPartMap.end() == it)
	{
		// Atlas part is not in the map, add it
		it = atlasPartMap.insert(
			pair<string, VTTImage*>( 
			currentPartName,
			new VTTImage( currentPartPath ) )
			).first;
	}

	if (!(*it).second->HasInternalData())
	{
		(*it).second->Load();
	}

	const unsigned char *data_pointer = (*it).second->GetDataPointer();

	return data_pointer;
}

/**
  *	Scales a square RGB-image to the half by using a gaussian filter kernel. The
  * parameters of the Configuration for the size and sigma of the kernel are used.
  *
  * @param dest pointer to the buffer that is used to store the calculated scaled image
  * @param src pointer to the RGB-image that is scaled and then stored in dest
  * @param srcWidth side length of the source image
  */
void VTTTileStore::ScaleRGBImageToTheHalf( unsigned char *dest, const unsigned char *src,
	UINT32 srcWidth)
{
	const UINT32 NUM_COMPONENTS = 3U;
	const UINT32 NUM_PIXELS = srcWidth * srcWidth;
	const UINT32 NUM_ELEMENTS = NUM_COMPONENTS * NUM_PIXELS;

	const UINT32 LINE_INC = srcWidth * NUM_COMPONENTS;

	const UINT32 RED = 0U;
	const UINT32 GREEN = 1U;
	const UINT32 BLUE = 2U;

	const UINT32 KERNEL_SIZE = mAtlas->GetConfiguration().GetKernelSize();
	const float KERNEL_SIGMA = mAtlas->GetConfiguration().GetKernelSigma();
	const UINT32 KERNEL_OFFSET = KERNEL_SIZE % 2 == 0 ? ((KERNEL_SIZE / 2U) - 1U) : ((KERNEL_SIZE - 1U) / 2U);
	const long long KERNEL_INIT_POS_VERTICAL = -static_cast<long long>( LINE_INC ) * KERNEL_OFFSET;
	const long long KERNEL_INIT_POS_HORIZONTAL = -static_cast<long long>( NUM_COMPONENTS ) * KERNEL_OFFSET;

	float *kernel_vertical = new float[KERNEL_SIZE];
	float *kernel_horizontal = kernel_vertical;

	float *vertical_filtered_image = new float[NUM_ELEMENTS];
	
	// Store the final filtered and scaled image directly into dest
/*	float *filtered_image = new float[NUM_ELEMENTS / 4U];*/

	float current_value[NUM_COMPONENTS] = { 0.0f, 0.0f, 0.0f };

	long long *current_kernel_position = new long long[KERNEL_SIZE];
		
	GetGaussianFilterKernel( 1U, KERNEL_SIZE, KERNEL_SIGMA, kernel_vertical );

	UINT32 current_min_index = 0U;
	UINT32 current_max_index = NUM_ELEMENTS - NUM_COMPONENTS;

	UINT32 line_counter = 0U;

	// Initialize kernel positions for the vertical filtering
	for (long long i = 0, pos = KERNEL_INIT_POS_VERTICAL; i < KERNEL_SIZE; i++, pos += LINE_INC)
	{
		current_kernel_position[i] = pos;
	}

	// Filter with the vertical kernel
	for (UINT32 i = 0; i < NUM_ELEMENTS; i += NUM_COMPONENTS)
	{
		current_value[RED] = 0.0f;
		current_value[GREEN] = 0.0f;
		current_value[BLUE] = 0.0f;

		for (UINT32 j = 0; j < KERNEL_SIZE; j++)
		{
			if (current_kernel_position[j] >= current_min_index && current_kernel_position[j] <= current_max_index)
			{
				current_value[RED] += src[current_kernel_position[j] + RED] * kernel_vertical[j];
				current_value[GREEN] += src[current_kernel_position[j] + GREEN] * kernel_vertical[j];
				current_value[BLUE] += src[current_kernel_position[j] + BLUE] * kernel_vertical[j];
			}

			current_kernel_position[j] += NUM_COMPONENTS;
		}

		vertical_filtered_image[i + RED] = current_value[RED];
		vertical_filtered_image[i + GREEN] = current_value[GREEN];
		vertical_filtered_image[i + BLUE] = current_value[BLUE];
	}

	// Initialize kernel positions for the horizontal filtering
	for (long long i = 0, pos = KERNEL_INIT_POS_HORIZONTAL; i < KERNEL_SIZE; i++, pos += NUM_COMPONENTS)
	{
		current_kernel_position[i] = pos;
	}

	current_min_index = 0U;
	current_max_index = LINE_INC - NUM_COMPONENTS;
	line_counter = 0U;

	// Filter with the horizontal kernel
	for (UINT32 i = 0, dest_index = 0; i < NUM_ELEMENTS; i += 2 * NUM_COMPONENTS, dest_index += NUM_COMPONENTS)
	{
		current_value[RED] = 0.0f;
		current_value[GREEN] = 0.0f;
		current_value[BLUE] = 0.0f;

		for (UINT32 j = 0; j < KERNEL_SIZE; j++)
		{
			if (current_kernel_position[j] >= current_min_index && current_kernel_position[j] <= current_max_index)
			{
				current_value[RED] += vertical_filtered_image[current_kernel_position[j] + RED] * kernel_horizontal[j];
				current_value[GREEN] += vertical_filtered_image[current_kernel_position[j] + GREEN] * kernel_horizontal[j];
				current_value[BLUE] += vertical_filtered_image[current_kernel_position[j] + BLUE] * kernel_horizontal[j];
			}

			current_kernel_position[j] += 2 * NUM_COMPONENTS;
		}

// 		filtered_image[dest_index + RED] = current_value[RED];
// 		filtered_image[dest_index + GREEN] = current_value[GREEN];
// 		filtered_image[dest_index + BLUE] = current_value[BLUE];

		dest[dest_index + RED] = static_cast<unsigned char>( floor( current_value[RED] + 0.5f ) );
		dest[dest_index + GREEN] = static_cast<unsigned char>( floor( current_value[GREEN] + 0.5f ) );
		dest[dest_index + BLUE] = static_cast<unsigned char>( floor( current_value[BLUE] + 0.5f ) );

		line_counter += 2U;

		if (line_counter == srcWidth)
		{
			// End of line
			current_min_index += 2 * LINE_INC;
			current_max_index += 2 * LINE_INC;
			i += LINE_INC;
			line_counter = 0U;

			for (UINT32 j = 0; j < KERNEL_SIZE; j++)
			{
				current_kernel_position[j] += LINE_INC;
			}
		}
	}

	// Clean up
	delete [] vertical_filtered_image;
/*	delete [] filtered_image;*/
	delete [] kernel_vertical;
	delete [] current_kernel_position;

	// Noob Method

// 	UINT32 line_inc = srcWidth * NUM_COMPONENTS;
// 	UINT32 src_upper_line_index = 0;
// 	UINT32 src_lower_line_index = line_inc;
// 
// 	UINT32 blocks_in_line = srcWidth / 2;
// 	UINT32 blocks_counter = 0;
// 
// 	float rgb[3];
// 
// 	for (UINT32 dest_index = 0;
// 		dest_index < ((srcWidth * srcWidth) / 4) * NUM_COMPONENTS;
// 		dest_index += 3)
// 	{
// 		//Red
// 		rgb[0] = (static_cast<float>( src[src_upper_line_index] ) +
// 			static_cast<float>( src[(src_upper_line_index++) + NUM_COMPONENTS] ) +
// 			static_cast<float>( src[src_lower_line_index] ) +
// 			static_cast<float>( src[(src_lower_line_index++) + NUM_COMPONENTS] )) * 0.25f;
// 
// 		//Green
// 		rgb[1] = (static_cast<float>( src[src_upper_line_index] ) +
// 			static_cast<float>( src[(src_upper_line_index++) + NUM_COMPONENTS] ) +
// 			static_cast<float>( src[src_lower_line_index] ) +
// 			static_cast<float>( src[(src_lower_line_index++) + NUM_COMPONENTS] )) * 0.25f;
// 
// 		//Blue
// 		rgb[2] = (static_cast<float>( src[src_upper_line_index] ) +
// 			static_cast<float>( src[(src_upper_line_index++) + NUM_COMPONENTS] ) +
// 			static_cast<float>( src[src_lower_line_index] ) +
// 			static_cast<float>( src[(src_lower_line_index++) + NUM_COMPONENTS] )) * 0.25f;
// 
// 		src_upper_line_index += NUM_COMPONENTS;
// 		src_lower_line_index += NUM_COMPONENTS;
// 
// 		blocks_counter++;
// 
// 		if (blocks_counter == blocks_in_line)
// 		{
// 			blocks_counter = 0;
// 			src_upper_line_index += line_inc;
// 			src_lower_line_index += line_inc;
// 		}
// 
// 		// Set final pixel value
// 		dest[dest_index] = static_cast<unsigned char>( floor( rgb[0] + 0.5f ) );
// 		dest[dest_index + 1] = static_cast<unsigned char>( floor( rgb[1] + 0.5f ) );
// 		dest[dest_index + 2] = static_cast<unsigned char>( floor( rgb[2] + 0.5f ) );
// 	}
}